package com.google.code.jholidays.core;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

import com.google.code.jholidays.events.AbstractEvent;
import com.google.code.jholidays.events.IEvent;
import com.google.code.jholidays.io.IDescriptorReader;

/**
 * Represents collection of {@link EventDescriptor} based on {@link ArrayList}.
 * This collection is used by {@link EventBuilder} in order to create
 * {@link IEvent} instances
 * <p>
 * Provides special methods of validation of collection. If you only need to
 * check whether collection is consistent see {@link #isConsistent()}
 * <p>
 * If you need to get list of all invalid event descriptors see
 * {@link #getInvalidDescriptors()}
 * 
 * @see EventDescriptor
 * @see EventBuilder
 * @see IDescriptorReader
 * @author tillias
 * 
 */
public class DescriptorCollection extends ArrayList<EventDescriptor> {

    private static final long serialVersionUID = -1931776967230597118L;

    /**
     * Creates new empty collection
     */
    public DescriptorCollection() {
	super();
    }

    /**
     * Creates new collection using existing one
     */
    public DescriptorCollection(Collection<EventDescriptor> collection) {
	super(collection);
    }

    /**
     * Validates that this collection is consistent, e.g. each
     * {@link EventDescriptor} has parent descriptor stored in this collection
     * 
     * @return True if collection is consistent, false otherwise
     * @see #getInvalidDescriptors()
     */
    public boolean isConsistent() {
	return getInvalidDescriptors().size() == 0;
    }

    /**
     * Gets list of all descriptors those having no parent ones.
     * <p>
     * If descriptor is not root (see {@link EventDescriptor#isRoot()}) or
     * descriptor has no parent it is considered to be invalid since we can't
     * create instance of {@link AbstractEvent} using such a descriptor
     * 
     * @return List of all invalid event descriptors
     * @see #isConsistent()
     */
    public List<EventDescriptor> getInvalidDescriptors() {
	List<EventDescriptor> result = new ArrayList<EventDescriptor>();

	if (size() != 0) {

	    // build set of all descriptor identifiers in this list
	    HashSet<Integer> identifiers = new HashSet<Integer>();

	    for (EventDescriptor e : this) {
		identifiers.add(e.getID());
	    }

	    /*
	     * now when we have all identifiers and can verify that each
	     * EventDescriptor has parent
	     */
	    for (EventDescriptor e : this) {
		if (!validateDescriptor(e, identifiers)) {
		    result.add(e);
		}

	    }
	}

	return result;
    }

    private boolean validateDescriptor(EventDescriptor e,
	    HashSet<Integer> identifiers) {
	return !dependsOnItself(e) && hasParent(e.getParentID(), identifiers);
    }

    private boolean hasParent(int parentID, HashSet<Integer> identifiers) {
	if (parentID == EventDescriptor.ROOT_ID
		|| identifiers.contains(parentID))
	    return true;

	return false;
    }

    private boolean dependsOnItself(EventDescriptor e) {
	return e.getID() == e.getParentID();
    }
}
